﻿using DumbPrograms.ChromeDevTools;
using DumbPrograms.ChromeDevTools.Protocol;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ZFBActivation.Log;


namespace ZFBActivation.Core
{
    public enum OpenMode
    {
        无头 = 0, 有头但不显示不加载图片 = 1, 有头并显示 = 2, 有头并显示并加载图片 = 3, 有头不显示并加载图片 = 4,
    }
    public class ChromeHelper
    {


        private const string ChromeAppKey = @"\Software\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe";
        public static string FindChromeExecutable() {
            List<string> list = new List<string>();
            string[] array = new string[4] { "PROGRAMFILES", "PROGRAMFILES(X86)", "LOCALAPPDATA", "PROGRAMW6432" };
            string[] array2 = new string[6] { "Google Chrome\\Chrome\\Application", "Google Chrome\\Chrome\\app", "Google\\Chrome\\Application", "Google\\Chrome\\app", "Google\\Chrome Beta\\Application", "Google\\Chrome Canary\\Application" };
            foreach (string variable in array) {
                foreach (string path in array2) {
                    string environmentVariable = Environment.GetEnvironmentVariable(variable);
                    if (environmentVariable != null) {
                        list.Add(System.IO.Path.Combine(environmentVariable, path, "chrome.exe"));
                    }
                }
            }

            string applocal = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);

            foreach (string path in array2) {
                list.Add(System.IO.Path.Combine(applocal, path, "chrome.exe"));
            }
            foreach (string item in list) {
                if (File.Exists(item)) {
                    return item;
                }
            }

            string chromeAppFileName = (string)(Registry.GetValue("HKEY_LOCAL_MACHINE" + ChromeAppKey, "", null) ?? Registry.GetValue("HKEY_CURRENT_USER" + ChromeAppKey, "", null));
            // 如果未找到谷歌浏览器则使用默认浏览器打开
            if (!String.IsNullOrWhiteSpace(chromeAppFileName)) {
                // 打开谷歌浏览器
                return chromeAppFileName;
            }

            return null;
        }



        private ChromeProcessHelper chrome;
        private int NewTabCount;
        private DevToolsClient devTools;

        private bool RunChromeProcess(ChromeSet chromeSet) {
            var result = true;
            try {
                //string app = SettingHelper.SysSetting.BrowserPath;
                string app = FindChromeExecutable();

                string additionalArguments = string.Empty;
                string cachePath = Path.Combine(Environment.CurrentDirectory, "cache", chromeSet.CacheKey);
                if (!Directory.Exists(cachePath)) {
                    try {
                        Directory.CreateDirectory(cachePath);
                    }
                    catch {
                    }
                }
                if (!string.IsNullOrEmpty(cachePath)) {
                    cachePath = $"--disk-cache-dir={cachePath}";
                }
                string userdata = string.Empty;

                string userPath = Path.Combine(Environment.CurrentDirectory, "user", chromeSet.CacheKey);
                if (!Directory.Exists(userPath)) {
                    try {
                        Directory.CreateDirectory(userPath);
                    }
                    catch {
                    }
                }
                userPath = $"--user-data-dir={userPath}";


                //--user-data-dir





                string proxyServer = string.Empty;
                if (!string.IsNullOrEmpty(chromeSet.ProxyUrl)) {
                    proxyServer = $"--proxy-server=\"{chromeSet.ProxyUrl}\"";
                }
                switch (chromeSet.Mode) {
                    case OpenMode.无头:
                        chrome = ChromeProcessHelper.StartNew(app, headless: true, additionalArguments: $"{proxyServer} {cachePath} {userPath} --disable-web-security --no-default-browser-check --no-first-run --no-sandbox --test-type  --incognito --no-startup-window");
                        break;
                    case OpenMode.有头并显示:
                        chrome = ChromeProcessHelper.StartNew(app, headless: false, additionalArguments: $"{proxyServer} {cachePath} {userPath} --window-size={chromeSet.Width},{chromeSet.Height} --window-position={chromeSet.PositionX},{chromeSet.PositionY} --blink-settings=imagesEnabled=false --disable-web-security  --no-default-browser-check --no-first-run --no-sandbox --test-type --no-startup-window");
                        break;
                    case OpenMode.有头但不显示不加载图片:
                        chromeSet.PositionY = 20000;
                        chromeSet.PositionX = 20000;
                        chrome = ChromeProcessHelper.StartNew(app, headless: false, additionalArguments: $"{proxyServer} {cachePath} {userPath} --window-size={chromeSet.Width},{chromeSet.Height} --window-position={chromeSet.PositionX},{chromeSet.PositionY} --blink-settings=imagesEnabled=false --disable-web-security  --no-default-browser-check --no-first-run --no-sandbox --test-type --no-startup-window");
                        break;
                    case OpenMode.有头并显示并加载图片:
                        chrome = ChromeProcessHelper.StartNew(app,debuggingPort: chromeSet.prot, headless: false, additionalArguments: $"{proxyServer} {cachePath} {userPath} --window-size={chromeSet.Width},{chromeSet.Height} --window-position={chromeSet.PositionX},{chromeSet.PositionY} --no-default-browser-check  --no-first-run --no-startup-window");
                        break;
                    case OpenMode.有头不显示并加载图片:
                        chromeSet.PositionY = 20000;
                        chromeSet.PositionX = 20000;
                        chrome = ChromeProcessHelper.StartNew(app, headless: false, additionalArguments: $"{proxyServer} {cachePath} {userPath} --window-size={chromeSet.Width},{chromeSet.Height} --window-position={chromeSet.PositionX},{chromeSet.PositionY} --disable-web-security  --no-default-browser-check --no-first-run --no-sandbox --test-type --no-startup-window");
                        break;
                }
                chrome.ProcessExited += Chrome_ProcessExited;
                Thread.Sleep(2000);
                devTools = new DevToolsClient(chrome.DebuggingPort);

                result = true;
            }
            catch (Exception ex) {
                LogHelper.Log(LogDefine.LogError, ex, "启动浏览器失败");
                result = false;
            }
            return result;

        }
        private void Chrome_ProcessExited(object sender, EventArgs e) {
            ChromeDispose();
            pageOpen.Clear();
        }

		public async ValueTask<InspectorClient> CreatePage(ChromeSet chromeSet) {
			if(!RunChromeProcess(chromeSet)) {
				return null;
			}
			InspectionTarget temp = await devTools.NewTab("chrome://newtab");

            if(temp == null) {
                return  null;
            }

            InspectorClient TabPage = await devTools.Inspect(temp);
			TabPage.ID = temp.Id;
			TabPage.OnClose += TabPage_OnClose;
			Interlocked.Increment(ref NewTabCount);
			//await TabPage.Security.SetIgnoreCertificateErrors(true);
			//await SetHeadless(TabPage);
			if(!pageOpen.ContainsKey(TabPage.ID)) {
                pageOpen.Add(TabPage.ID, false);
            }
            return TabPage;
        }



        private void TabPage_OnClose(object? sender, EventArgs e) {
            Debug.WriteLine("Dev 链接断开New");
            InspectorClient inspector = sender as InspectorClient;
            CloseWebView(inspector);
        }

        private async Task SetHeadless(InspectorClient TabPage) {
            await TabPage.Page.AddScriptToEvaluateOnNewDocument(@"
                            Object.defineProperty(navigator, 'maxTouchPoints', {get: () => 1});
                            Object.defineProperty(navigator.connection, 'rtt', {get: () => 100});
							alert = function(){}
                            // https://github.com/microlinkhq/browserless/blob/master/packages/goto/src/evasions/chrome-runtime.js
                            window.chrome = {
                                app: {
                                    isInstalled: false,
                                    InstallState: {
                                        DISABLED: 'disabled',
                                        INSTALLED: 'installed',
                                        NOT_INSTALLED: 'not_installed'
                                    },
                                    RunningState: {
                                        CANNOT_RUN: 'cannot_run',
                                        READY_TO_RUN: 'ready_to_run',
                                        RUNNING: 'running'
                                    }
                                },
                                runtime: {
                                    OnInstalledReason: {
                                        CHROME_UPDATE: 'chrome_update',
                                        INSTALL: 'install',
                                        SHARED_MODULE_UPDATE: 'shared_module_update',
                                        UPDATE: 'update'
                                    },
                                    OnRestartRequiredReason: {
                                        APP_UPDATE: 'app_update',
                                        OS_UPDATE: 'os_update',
                                        PERIODIC: 'periodic'
                                    },
                                    PlatformArch: {
                                        ARM: 'arm',
                                        ARM64: 'arm64',
                                        MIPS: 'mips',
                                        MIPS64: 'mips64',
                                        X86_32: 'x86-32',
                                        X86_64: 'x86-64'
                                    },
                                    PlatformNaclArch: {
                                        ARM: 'arm',
                                        MIPS: 'mips',
                                        MIPS64: 'mips64',
                                        X86_32: 'x86-32',
                                        X86_64: 'x86-64'
                                    },
                                    PlatformOs: {
                                        ANDROID: 'android',
                                        CROS: 'cros',
                                        LINUX: 'linux',
                                        MAC: 'mac',
                                        OPENBSD: 'openbsd',
                                        WIN: 'win'
                                    },
                                    RequestUpdateCheckStatus: {
                                        NO_UPDATE: 'no_update',
                                        THROTTLED: 'throttled',
                                        UPDATE_AVAILABLE: 'update_available'
                                    }
                                }
                            }
                            // https://github.com/microlinkhq/browserless/blob/master/packages/goto/src/evasions/navigator-permissions.js
                            if (!window.Notification) {
                                window.Notification = {
                                    permission: 'denied'
                                }
                            }
                            const originalQuery = window.navigator.permissions.query
                            window.navigator.permissions.__proto__.query = parameters =>
                                parameters.name === 'notifications'
                                    ? Promise.resolve({ state: window.Notification.permission })
                                    : originalQuery(parameters)
                            const oldCall = Function.prototype.call
                            function call() {
                                return oldCall.apply(this, arguments)
                            }
                            Function.prototype.call = call
                            const nativeToStringFunctionString = Error.toString().replace(/Error/g, 'toString')
                            const oldToString = Function.prototype.toString
                            function functionToString() {
                                if (this === window.navigator.permissions.query) {
                                    return 'function query() { [native code] }'
                                }
                                if (this === functionToString) {
                                    return nativeToStringFunctionString
                                }
                                return oldCall.call(oldToString, this)
                            }
                            // eslint-disable-next-line
                            Function.prototype.toString = functionToString
                         ");
        }

        public void CloseWebView(InspectorClient TabPage) {
            if (TabPage != null) {
                try {
                    pageOpen[TabPage.ID] = true;
                    pageOpen.Remove(TabPage.ID);
                    TabPage.Dispose();

                }
                catch { }
            }
            if (pageOpen.All(t => t.Value)) {
                ChromeDispose();
            }
        }

        public void ChromeDispose() {
            if (devTools != null) {
                devTools = null;
            }
            if (chrome != null) {
                chrome.ProcessExited -= Chrome_ProcessExited;
                chrome.Dispose();
            }
            pageOpen.Clear();
        }







        private Dictionary<string, bool> pageOpen = new Dictionary<string, bool>();




    }

    public class ChromeSet
    {
        public int prot { get; set;}

        public int Width { get; set; } = 1600;
        public int Height { get; set; } = 900;

        public int PositionX { get; set; } = 0;
        public int PositionY { get; set; } = 0;
        public string CacheKey { get; set; }
        public OpenMode Mode { get; set; } = OpenMode.有头并显示并加载图片;
        public string ProxyUrl {  get; set; }
    }

}
